package com.growthgate.bootcommon.util;

import com.google.common.collect.Maps;
import com.growthgate.bootcommon.common.Constants;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.jetbrains.annotations.NotNull;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Url工具类
 *
 * @author GrowthGate
 * @date 2019/11/11
 */
public class UrlUtils {

    /**
     * url编码
     *
     * @param url url地址
     * @return 编码后的url地址
     */
    @NotNull
    public static String encode(@NotNull String url) {
        try {
            return URLEncoder.encode(url, Constants.DEFAULT_CHARSET);
        } catch (UnsupportedEncodingException e) {
            return url;
        }
    }

    /**
     * url解码
     *
     * @param url url地址
     * @return 解码后的url地址
     */
    @NotNull
    public static String decode(@NotNull String url) {
        try {
            return URLDecoder.decode(url, Constants.DEFAULT_CHARSET);
        } catch (UnsupportedEncodingException e) {
            return url;
        }
    }

    @NotNull
    public static String getNoParamsUrl(@NotNull String url) {
        return new Url(url).getUrl();
    }

    @NotNull
    public static Map<String, List<String>> getQueryParams(@NotNull String url) {
        return new Url(url).getQueryParams();
    }

    @NotNull
    public static String addQueryParams(@NotNull String url, @NotNull Map<String, List<Object>> queryParams) {
        Url u = new Url(url);
        for (Map.Entry<String, List<Object>> entry : queryParams.entrySet()) {
            List<Object> list = entry.getValue();
            if (list != null) {
                for (Object o : list) {
                    if (o != null) {
                        u.putQueryParam(entry.getKey(), o);
                    }
                }
            }
        }
        return u.toString();
    }

    @NotNull
    public static String addQueryParam(@NotNull String url, @NotNull String key, @NotNull Object value) {
        return new Url(url).putQueryParam(key, value).toString();
    }

    @Accessors(chain = true)
    static class Url {

        private static final char URL_PARAM_SYMBOL = '?';
        private static final char URL_PARAM_SEPARATOR_SYMBOL = '&';
        private static final char URL_VALUE_SYMBOL = '=';

        @Getter
        @Setter
        @NonNull
        private String url;

        @Getter
        private Map<String, List<String>> queryParams = Maps.newLinkedHashMapWithExpectedSize(1);

        public Url(@NotNull String url) {
            String[] split = StringUtils.split(url, URL_PARAM_SYMBOL);
            if (split.length <= 1) {
                this.url = url;
            } else {
                this.url = split[0];
                String[] params = StringUtils.split(split[1], URL_PARAM_SEPARATOR_SYMBOL);
                for (String param : params) {
                    String[] keyValue = StringUtils.split(param, URL_VALUE_SYMBOL);
                    if (keyValue.length > 1) {
                        String key = keyValue[0];
                        if (StringUtils.isNotBlank(key)) {
                            putQueryParam(decode(key), decode(keyValue[1]));
                        }
                    }
                }
            }
        }

        @NotNull
        public UrlUtils.Url putQueryParam(@NotNull String key, @NotNull Object value) {
            List<String> values = queryParams.computeIfAbsent(key, k -> new ArrayList<>());
            values.add(value.toString());
            return this;
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder(url);
            if (!queryParams.isEmpty()) {
                sb.append(URL_PARAM_SYMBOL);
                for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {
                    if (StringUtils.isNotBlank(entry.getKey()) && CollectionUtils.isNotEmpty(entry.getValue())) {
                        String key = entry.getKey();
                        for (String value : entry.getValue()) {
                            if (value != null) {
                                sb.append(encode(key)).append(URL_VALUE_SYMBOL).append(encode(value)).append(URL_PARAM_SEPARATOR_SYMBOL);
                            }
                        }
                    }
                }
                if (sb.charAt(sb.length() - 1) == URL_PARAM_SEPARATOR_SYMBOL) {
                    sb.deleteCharAt(sb.length() - 1);
                }
            }
            return sb.toString();
        }

    }

}